package com.iwhalecloud.bss.kite.cucc.order.accept.constructor.inst;

import com.google.common.collect.ImmutableMap;
import com.iwhalecloud.bss.kite.common.util.KiteBeanUtils;
import com.iwhalecloud.bss.kite.common.util.KiteDateUtils;
import com.iwhalecloud.bss.kite.common.util.KiteMapUtils;
import com.iwhalecloud.bss.kite.common.util.KiteStringUtils;
import com.iwhalecloud.bss.kite.cucc.order.accept.constructor.DataConstructorFactory;
import com.iwhalecloud.bss.kite.cucc.order.accept.constructor.IDataConstructor;
import com.iwhalecloud.bss.kite.cucc.order.loader.IInstLoader;
import com.iwhalecloud.bss.kite.cucc.order.loader.InstLoaderFactory;
import com.iwhalecloud.bss.kite.cucc.order.util.AcceptUtils;
import com.iwhalecloud.bss.kite.dataservice.entity.inst.IInst;
import com.iwhalecloud.bss.kite.manager.enums.ActionType;
import com.iwhalecloud.bss.kite.manager.enums.InstType;
import com.iwhalecloud.bss.kite.manager.inst.KiteAttr;
import com.iwhalecloud.bss.kite.manager.inst.KiteInst;
import com.iwhalecloud.bss.kite.manager.inst.change.KiteInstChange;
import com.iwhalecloud.bss.kite.manager.util.ManagerUtils;
import com.iwhalecloud.bss.kite.order.client.dto.InstContainer;
import com.iwhalecloud.bss.kite.order.client.dto.OrderRequest;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;

import java.beans.PropertyDescriptor;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;

/**
 * 实例构造器
 * @param <K>
 * @param <I>
 */
public abstract class InstConstructor<K extends KiteInst, I extends IInst> implements IDataConstructor<String, I> {

    /**
     * 获取实例表名
     * @return
     */
    public abstract String getTableName();

    /**
     * 获取父实例类型
     * @return
     */
    public InstType getParentInstType() {
        return null;
    };

    /**
     * new一个新增的实例(actionType=A)
     * @param orderRequest
     * @param kiteInst
     * @return
     */
    public abstract I newInst(OrderRequest orderRequest, K kiteInst);

    @Override
    public boolean contains(OrderRequest orderRequest, String parentInstId, String instId, Map<String, Object> params) {
        if (Objects.isNull(orderRequest) || StringUtils.isBlank(instId)) {
            return false;
        }

        return Objects.nonNull(InstContainer.ofRequest(orderRequest).getInst(getTableName(), instId));
    }

    /**
     * 把kiteInst实例转换成数据库实例
     * @param orderRequest
     * @param kiteInst
     * @param params
     * @return
     */
    @SuppressWarnings("unchecked")
    public I convert(OrderRequest orderRequest, K kiteInst, Map<String, Object> params) {
        if (!ObjectUtils.allNotNull(orderRequest, kiteInst) || Objects.equals(Boolean.TRUE, kiteInst.getIsVirtualInst())
            || isIneffectiveInst(kiteInst)) {
            return null;
        }

        IInst inst = null;
        if (isAddByCurrent(orderRequest, kiteInst)) {
            inst = newInst(orderRequest, kiteInst);
        }
        else {
            String custId = kiteInst.getOwnerCustId();
            custId = StringUtils.isNotBlank(custId) ? custId : orderRequest.getCustId();
            IInstLoader<IInst> instLoader = InstLoaderFactory.getInstLoader(getTableName());
            inst = instLoader.getInst(getTableName(), kiteInst.getInstId(), custId);
            if (Objects.nonNull(inst)) {
                if (Objects.equals(ActionType.D, kiteInst.getActionType())) {
                    InstType parentInstType = KiteMapUtils.getValue(params, KEY_PARENT_INST_TYPE);
                    String parentInstId = KiteMapUtils.getValue(params, KEY_PARENT_INST_ID);
                    dismantleInst(orderRequest, inst, kiteInst.getInstType(), kiteInst.getInstId(), parentInstType,
                        parentInstId);
                }
                else {
                    boolean change = setChangeAttrs(orderRequest, kiteInst, inst);
                    if (Objects.equals(ActionType.A.name(), inst.getActionType())) {
                        // 新增未竣工的actionType可能是A，如果有变动信息，actionType改为M，否则改为K
                        String actionType = change ? ActionType.M.name() : ActionType.K.name();
                        AcceptUtils.setActionType(inst, actionType);
                    }
                    else if (!Objects.equals(ActionType.A, kiteInst.getActionType())) {
                        AcceptUtils.setActionType(inst, ManagerUtils.getEnumName(kiteInst.getActionType()));
                    }
                }
            }
            else {
                inst = newInst(orderRequest, kiteInst);
            }
        }

        // 修改了子实例，如果父实例没有其他变动，父实例的actionType需要改成M
        String actionType = KiteMapUtils.getString(params, KiteInst.ACTION_TYPE);
        if (StringUtils.isNotBlank(actionType)
            && StringUtils.equalsAny(inst.getActionType(), ActionType.A.name(), ActionType.K.name())) {
            AcceptUtils.setActionType(inst, actionType);
        }

        return (I) inst;
    }

    /**
     * 获取父实例构造器
     * @return
     */
    @SuppressWarnings("unchecked")
    public <S extends KiteInst, U extends IInst, T extends InstConstructor<S, U>> T getParentInstConstructor() {
        IDataConstructor<String, U> dataConstructor = DataConstructorFactory.getInstConstructor(getParentInstType());
        return (T) dataConstructor;
    }

    /**
     * 构造父实例
     * @param orderRequest
     * @param parentInstId
     */
    @SuppressWarnings("unchecked")
    public <T extends IInst> T buildParentInst(OrderRequest orderRequest, String parentInstId) {
        InstConstructor<KiteInst, IInst> instConstructor = getParentInstConstructor();
        if (!instConstructor.contains(orderRequest, "", parentInstId, null)) {
            return (T) instConstructor.build(orderRequest, parentInstId,
                ImmutableMap.of(KiteInst.ACTION_TYPE, ActionType.M.name()));
        }
        else {
            return InstContainer.ofRequest(orderRequest).getInst(instConstructor.getTableName(), parentInstId);
        }
    }

    /**
     * 把动态属性值设置到实例中
     * @param inst
     * @param attrMap
     * @param tableNames
     */
    public void setAttrs(IInst inst, Map<String, KiteAttr> attrMap, Set<String> tableNames) {
        if (!ObjectUtils.allNotNull(inst, attrMap, tableNames)) {
            return;
        }

        Set<Entry<String, KiteAttr>> entrySet = attrMap.entrySet();
        for (Entry<String, KiteAttr> entry : entrySet) {
            KiteAttr attr = entry.getValue();
            if (StringUtils.isNotBlank(attr.getTableName())
                && !tableNames.contains(StringUtils.upperCase(attr.getTableName()))) {
                continue;
            }

            String propertyName = KiteStringUtils.underlineToCamel(attr.getFieldName());
            if (AcceptUtils.isAddInst(inst)) {
                // 如果实例上已经设置了值，但是动态属性中的值是空的，不覆盖(例如ownerCustId)
                Object value = KiteBeanUtils.getValueByName(inst, propertyName);
                if (Objects.nonNull(value) && StringUtils.isBlank(attr.getValue())) {
                    continue;
                }
            }

            String value = attr.getValue();
            // 空字符串转换成null，防止把空字符串set到number类型的字段入库时报错
            value = StringUtils.isNotBlank(value) ? value : null;
            KiteBeanUtils.setValueByName(inst, propertyName, getStringOrDate(inst, propertyName, value));
        }
    }

    /**
     * 根据变动信息修改实例的属性值
     * @param orderRequest
     * @param kiteInst
     * @param inst
     */
    public boolean setChangeAttrs(OrderRequest orderRequest, KiteInst kiteInst, IInst inst) {
        boolean change = false;
        if (!ObjectUtils.allNotNull(orderRequest, kiteInst, inst)) {
            return change;
        }

        String key = ManagerUtils.join(kiteInst.getInstType(), kiteInst.getInstId());
        List<KiteInstChange> instChanges = orderRequest.getInstChangeMap().getOrDefault(key, new ArrayList<>());
        for (KiteInstChange instChange : instChanges) {
            if (!Objects.equals(ActionType.M, instChange.getActionType())
                || Objects.equals(KiteInst.ACTION_TYPE, instChange.getFieldName())) {
                continue;
            }

            String fieldName = instChange.getFieldName();
            String newValue = instChange.getNewValue();
            // 空字符串转换成null，防止把空字符串set到number类型的字段入库时报错
            newValue = StringUtils.isNotBlank(newValue) ? newValue : null;
            String propertyName = KiteStringUtils.underlineToCamel(fieldName);
            KiteBeanUtils.setValueByName(inst, propertyName, getStringOrDate(inst, propertyName, newValue));
            change = true;
        }

        return change;
    }

    /**
     * 如果属性是日期类型，则把value转换成日期类型返回
     * @param bean
     * @param propertyName
     * @param value
     * @return
     */
    private Object getStringOrDate(Object bean, String propertyName, String value) {
        if (Objects.isNull(bean) || StringUtils.isBlank(propertyName)) {
            return value;
        }

        PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(bean.getClass(), propertyName);
        if (Objects.isNull(propertyDescriptor)) {
            return value;
        }

        Class<?> propertyType = propertyDescriptor.getPropertyType();
        if (Objects.equals(Date.class, propertyType)) {
            if (StringUtils.isBlank(value)) {
                return null;
            }
            else {
                return KiteDateUtils.parseStrToDateTime(value);
            }
        }
        else if (Objects.equals(LocalDateTime.class, propertyType)) {
            if (StringUtils.isBlank(value)) {
                return null;
            }
            else {
                return LocalDateTime.parse(value, DateTimeFormatter.ofPattern(KiteDateUtils.DATE_TIME_FORMAT));
            }
        }

        return value;
    }

    /**
     * 判断实例是否本次新增的，即使actionType=A也有可能不是本次新增的(新增未竣工actionType也是A)
     * @param orderRequest
     * @param inst
     * @return
     */
    public boolean isAddByCurrent(OrderRequest orderRequest, KiteInst inst) {
        if (!ObjectUtils.allNotNull(orderRequest, inst)) {
            return false;
        }

        KiteInstChange addInstChange = null;
        String key = ManagerUtils.join(inst.getInstType(), inst.getInstId());
        List<KiteInstChange> instChanges = orderRequest.getInstChangeMap().getOrDefault(key, new ArrayList<>());
        for (KiteInstChange instChange : instChanges) {
            if (Objects.equals(inst.getInstId(), instChange.getSubInstId())
                && Objects.equals(inst.getInstType(), instChange.getSubInstType())
                && Objects.equals(ActionType.A, instChange.getActionType())
                && StringUtils.isBlank(instChange.getFieldName())) {
                addInstChange = instChange;
                break;
            }
        }

        return Objects.equals(ActionType.A, inst.getActionType()) && Objects.nonNull(addInstChange);
    }

    /**
     * 拆机/退订实例处理
     * <li>只保留actionType=D的变动信息，删除其他属性的变动信息
     * <li>如果没有变动信息，则补充一条actionType=D的变动信息
     * @param orderRequest
     * @param inst
     * @param instType
     * @param instId
     * @param parentInstType
     * @param parentInstId
     */
    public static void dismantleInst(OrderRequest orderRequest, IInst inst, InstType instType, String instId,
        InstType parentInstType, String parentInstId) {
        if (!ObjectUtils.allNotNull(orderRequest, inst, instType) || StringUtils.isBlank(instId)) {
            return;
        }

        String key = ManagerUtils.join(instType, instId);
        List<KiteInstChange> instChanges = orderRequest.getInstChangeMap().get(key);
        if (Objects.isNull(instChanges)) {
            instChanges = new ArrayList<>();
            orderRequest.getInstChangeMap().put(key, instChanges);
        }

        // 只保留actionType=D的变动信息，删除其他属性的变动信息
        instChanges.removeIf(instChange -> {
            return !(Objects.equals(ActionType.D, instChange.getActionType())
                && Objects.equals(KiteInst.ACTION_TYPE, instChange.getFieldName())
                && Objects.equals(ActionType.D.name(), instChange.getNewValue()));
        });

        // 如果没有变动信息，则补充一条actionType=D的变动信息
        if (instChanges.isEmpty()) {
            parentInstType = Objects.nonNull(parentInstType) ? parentInstType : instType;
            parentInstId = StringUtils.isNotBlank(parentInstId) ? parentInstId : instId;
            AcceptUtils.addInstChange(orderRequest, parentInstId, parentInstType, instId, instType, ActionType.D,
                KiteInst.ACTION_TYPE, ActionType.K.name(), ActionType.D.name());
        }

        AcceptUtils.setActionType(inst, ActionType.D.name());
    }

    /**
     * 判断实例是否有效的
     * @param inst
     * @return
     */
    public boolean isEffectiveInst(KiteInst inst) {
        return Objects.nonNull(inst) && !Objects.equals(ActionType.X, inst.getActionType());
    }

    /**
     * 判断实例是否无效的
     * @param inst
     * @return
     */
    public boolean isIneffectiveInst(KiteInst inst) {
        return !isEffectiveInst(inst);
    }
}
